Mac 端末のターミナルでミリ秒以下のタイムスタンプを取得したい
コンバンハ、千葉(幸)です。
date
コマンドでミリ秒以下のタイムスタンプを取得したい時がありました。
具体的には、以下のような出力を期待していました。
$ date +'%H:%M:%S.%3N' 19:34:20.571 #3桁のミリ秒まで取得できている
普段使いしている Mac 端末で実際にコマンドを実行してみると、以下の結果になりました。
$ date +'%H:%M:%S.%3N' 19:34:20.3N #%3Nのフォーマットが効かずそのまま3Nが出力されている
原因と対策を調べるのに15分くらいかかったので、同じような方の15分を節約するためにブログに残しておきます。
まとめ
- Linux のコマンドによっては BSD か GNU で挙動に差異がある
- macOS でデフォルトでインストールされているものは基本的に BSD 版
- date コマンドでミリ秒以下をフォーマット表示できるのは GNU 版
- Homebrew で
coreutils
をインストールすることで macOS でも多くの GNU 版のユーティリティを利用可能に
実行環境
- macOS Ventura 13.0.1
- zsh
BSD date コマンドはミリ秒以下をフォーマットできない
(主に)Linux 系で使用するコマンドには、同じ名前でも実装が異なるものあります。BSD 版か GNU 版か、を区別する機会が多いかと思います。
GNU date ではミリ秒以下のフォーマットに対応しています。
FORMAT で出力を制御します。解釈される文字列は次の通りです。
…….
- %N
ナノ秒 (000000000..999999999)
%3N
のように記載することで桁数を指定できます。
一方、BSD date では「秒」までしか対応していません。
If an operand does not have a leading plus sign, it is interpreted as a value for setting the system's notion of the current date and time. The canonical representation for setting the date and time is: cc Century (either 19 or 20) prepended to the abbreviated year. yy Year in abbreviated form (e.g., 89 for 1989, 06 for 2006). mm Numeric month, a number from 1 to 12. dd Day, a number from 1 to 31. HH Hour, a number from 0 to 23. MM Minutes, a number from 0 to 59. SS Seconds, a number from 0 to 60 (59 plus a potential leap second).
macOS に標準でインストールされているコマンドは基本的に BSD 版であり、BSD date がミリ秒以下に対応していないことから冒頭の結果になったというわけでした。
coreutils のインストールにより GNU date を利用可能に
macOS でも GNU 版の date コマンドを使いたくなります。その場合、Homebrew でcoreutils
をインストールするのが一番手っ取り早いかと思います。
(Homebrew のインストール自体がまだの場合は、以下を実行してください。)
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
Homebrew を使用できる状態で、以下を実行します。
$ brew install coreutils
インストールが完了すれば、GNU 版の date コマンドをgdate
として利用可能になります。
$ gdate --version date (GNU coreutils) 9.3 Copyright (C) 2023 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by David MacKenzie. $ gdate +'%H:%M:%S.%3N' 20:01:13.404
↑ミリ秒以下のフォーマットがきちんとできています。
参考:他にどんな GNU コマンドが使えるようになっているのか
coreutils
でインストールされたユーティリティはgdate
だけではありません。
その内訳を見るには、インストール時のログが参考になりそうです。
…… ==> Installing coreutils ==> Pouring coreutils--9.3.arm64_ventura.bottle.tar.gz ==> Caveats Commands also provided by macOS and the commands dir, dircolors, vdir have been installed with the prefix "g". If you need to use these commands with their normal names, you can add a "gnubin" directory to your PATH with: PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH" ……
ログの中で「接頭辞g
なしで GNU 版のコマンドを使用したい場合、追加してね」と表示されているパスを確認してみます。
$ ls -go /opt/homebrew/opt/coreutils/libexec/gnubin #-gと-oはそれぞれグループとオーナーの表示を省略するオプション total 0 lrwxr-xr-x 1 12 4 18 23:32 [ -> ../../bin/g[ lrwxr-xr-x 1 16 4 18 23:32 b2sum -> ../../bin/gb2sum lrwxr-xr-x 1 17 4 18 23:32 base32 -> ../../bin/gbase32 lrwxr-xr-x 1 17 4 18 23:32 base64 -> ../../bin/gbase64 lrwxr-xr-x 1 19 4 18 23:32 basename -> ../../bin/gbasename lrwxr-xr-x 1 17 4 18 23:32 basenc -> ../../bin/gbasenc lrwxr-xr-x 1 14 4 18 23:32 cat -> ../../bin/gcat lrwxr-xr-x 1 16 4 18 23:32 chcon -> ../../bin/gchcon lrwxr-xr-x 1 16 4 18 23:32 chgrp -> ../../bin/gchgrp lrwxr-xr-x 1 16 4 18 23:32 chmod -> ../../bin/gchmod lrwxr-xr-x 1 16 4 18 23:32 chown -> ../../bin/gchown lrwxr-xr-x 1 17 4 18 23:32 chroot -> ../../bin/gchroot lrwxr-xr-x 1 16 4 18 23:32 cksum -> ../../bin/gcksum lrwxr-xr-x 1 15 4 18 23:32 comm -> ../../bin/gcomm lrwxr-xr-x 1 13 4 18 23:32 cp -> ../../bin/gcp lrwxr-xr-x 1 17 4 18 23:32 csplit -> ../../bin/gcsplit lrwxr-xr-x 1 14 4 18 23:32 cut -> ../../bin/gcut lrwxr-xr-x 1 15 4 18 23:32 date -> ../../bin/gdate lrwxr-xr-x 1 13 4 18 23:32 dd -> ../../bin/gdd lrwxr-xr-x 1 13 4 18 23:32 df -> ../../bin/gdf lrwxr-xr-x 1 14 4 18 23:32 dir -> ../../bin/gdir lrwxr-xr-x 1 20 4 18 23:32 dircolors -> ../../bin/gdircolors lrwxr-xr-x 1 18 4 18 23:32 dirname -> ../../bin/gdirname lrwxr-xr-x 1 13 4 18 23:32 du -> ../../bin/gdu lrwxr-xr-x 1 15 4 18 23:32 echo -> ../../bin/gecho lrwxr-xr-x 1 14 4 18 23:32 env -> ../../bin/genv lrwxr-xr-x 1 17 4 18 23:32 expand -> ../../bin/gexpand lrwxr-xr-x 1 15 4 18 23:32 expr -> ../../bin/gexpr lrwxr-xr-x 1 17 4 18 23:32 factor -> ../../bin/gfactor lrwxr-xr-x 1 16 4 18 23:32 false -> ../../bin/gfalse lrwxr-xr-x 1 14 4 18 23:32 fmt -> ../../bin/gfmt lrwxr-xr-x 1 15 4 18 23:32 fold -> ../../bin/gfold lrwxr-xr-x 1 17 4 18 23:32 groups -> ../../bin/ggroups lrwxr-xr-x 1 15 4 18 23:32 head -> ../../bin/ghead lrwxr-xr-x 1 17 4 18 23:32 hostid -> ../../bin/ghostid lrwxr-xr-x 1 13 4 18 23:32 id -> ../../bin/gid lrwxr-xr-x 1 18 4 18 23:32 install -> ../../bin/ginstall lrwxr-xr-x 1 15 4 18 23:32 join -> ../../bin/gjoin lrwxr-xr-x 1 15 4 18 23:32 kill -> ../../bin/gkill lrwxr-xr-x 1 15 4 18 23:32 link -> ../../bin/glink lrwxr-xr-x 1 13 4 18 23:32 ln -> ../../bin/gln lrwxr-xr-x 1 18 4 18 23:32 logname -> ../../bin/glogname lrwxr-xr-x 1 13 4 18 23:32 ls -> ../../bin/gls lrwxr-xr-x 1 17 4 18 23:32 md5sum -> ../../bin/gmd5sum lrwxr-xr-x 1 16 4 18 23:32 mkdir -> ../../bin/gmkdir lrwxr-xr-x 1 17 4 18 23:32 mkfifo -> ../../bin/gmkfifo lrwxr-xr-x 1 16 4 18 23:32 mknod -> ../../bin/gmknod lrwxr-xr-x 1 17 4 18 23:32 mktemp -> ../../bin/gmktemp lrwxr-xr-x 1 13 4 18 23:32 mv -> ../../bin/gmv lrwxr-xr-x 1 15 4 18 23:32 nice -> ../../bin/gnice lrwxr-xr-x 1 13 4 18 23:32 nl -> ../../bin/gnl lrwxr-xr-x 1 16 4 18 23:32 nohup -> ../../bin/gnohup lrwxr-xr-x 1 16 4 18 23:32 nproc -> ../../bin/gnproc lrwxr-xr-x 1 17 4 18 23:32 numfmt -> ../../bin/gnumfmt lrwxr-xr-x 1 13 4 18 23:32 od -> ../../bin/god lrwxr-xr-x 1 16 4 18 23:32 paste -> ../../bin/gpaste lrwxr-xr-x 1 18 4 18 23:32 pathchk -> ../../bin/gpathchk lrwxr-xr-x 1 16 4 18 23:32 pinky -> ../../bin/gpinky lrwxr-xr-x 1 13 4 18 23:32 pr -> ../../bin/gpr lrwxr-xr-x 1 19 4 18 23:32 printenv -> ../../bin/gprintenv lrwxr-xr-x 1 17 4 18 23:32 printf -> ../../bin/gprintf lrwxr-xr-x 1 14 4 18 23:32 ptx -> ../../bin/gptx lrwxr-xr-x 1 14 4 18 23:32 pwd -> ../../bin/gpwd lrwxr-xr-x 1 19 4 18 23:32 readlink -> ../../bin/greadlink lrwxr-xr-x 1 19 4 18 23:32 realpath -> ../../bin/grealpath lrwxr-xr-x 1 13 4 18 23:32 rm -> ../../bin/grm lrwxr-xr-x 1 16 4 18 23:32 rmdir -> ../../bin/grmdir lrwxr-xr-x 1 17 4 18 23:32 runcon -> ../../bin/gruncon lrwxr-xr-x 1 14 4 18 23:32 seq -> ../../bin/gseq lrwxr-xr-x 1 18 4 18 23:32 sha1sum -> ../../bin/gsha1sum lrwxr-xr-x 1 20 4 18 23:32 sha224sum -> ../../bin/gsha224sum lrwxr-xr-x 1 20 4 18 23:32 sha256sum -> ../../bin/gsha256sum lrwxr-xr-x 1 20 4 18 23:32 sha384sum -> ../../bin/gsha384sum lrwxr-xr-x 1 20 4 18 23:32 sha512sum -> ../../bin/gsha512sum lrwxr-xr-x 1 16 4 18 23:32 shred -> ../../bin/gshred lrwxr-xr-x 1 15 4 18 23:32 shuf -> ../../bin/gshuf lrwxr-xr-x 1 16 4 18 23:32 sleep -> ../../bin/gsleep lrwxr-xr-x 1 15 4 18 23:32 sort -> ../../bin/gsort lrwxr-xr-x 1 16 4 18 23:32 split -> ../../bin/gsplit lrwxr-xr-x 1 15 4 18 23:32 stat -> ../../bin/gstat lrwxr-xr-x 1 17 4 18 23:32 stdbuf -> ../../bin/gstdbuf lrwxr-xr-x 1 15 4 18 23:32 stty -> ../../bin/gstty lrwxr-xr-x 1 14 4 18 23:32 sum -> ../../bin/gsum lrwxr-xr-x 1 15 4 18 23:32 sync -> ../../bin/gsync lrwxr-xr-x 1 14 4 18 23:32 tac -> ../../bin/gtac lrwxr-xr-x 1 15 4 18 23:32 tail -> ../../bin/gtail lrwxr-xr-x 1 14 4 18 23:32 tee -> ../../bin/gtee lrwxr-xr-x 1 15 4 18 23:32 test -> ../../bin/gtest lrwxr-xr-x 1 18 4 18 23:32 timeout -> ../../bin/gtimeout lrwxr-xr-x 1 16 4 18 23:32 touch -> ../../bin/gtouch lrwxr-xr-x 1 13 4 18 23:32 tr -> ../../bin/gtr lrwxr-xr-x 1 15 4 18 23:32 true -> ../../bin/gtrue lrwxr-xr-x 1 19 4 18 23:32 truncate -> ../../bin/gtruncate lrwxr-xr-x 1 16 4 18 23:32 tsort -> ../../bin/gtsort lrwxr-xr-x 1 14 4 18 23:32 tty -> ../../bin/gtty lrwxr-xr-x 1 16 4 18 23:32 uname -> ../../bin/guname lrwxr-xr-x 1 19 4 18 23:32 unexpand -> ../../bin/gunexpand lrwxr-xr-x 1 15 4 18 23:32 uniq -> ../../bin/guniq lrwxr-xr-x 1 17 4 18 23:32 unlink -> ../../bin/gunlink lrwxr-xr-x 1 17 4 18 23:32 uptime -> ../../bin/guptime lrwxr-xr-x 1 16 4 18 23:32 users -> ../../bin/gusers lrwxr-xr-x 1 15 4 18 23:32 vdir -> ../../bin/gvdir lrwxr-xr-x 1 13 4 18 23:32 wc -> ../../bin/gwc lrwxr-xr-x 1 14 4 18 23:32 who -> ../../bin/gwho lrwxr-xr-x 1 17 4 18 23:32 whoami -> ../../bin/gwhoami lrwxr-xr-x 1 14 4 18 23:32 yes -> ../../bin/gyes
↑例えばdate -> ../../bin/gdate
のように、各コマンドのリンクが張られていることがわかります。
リンク先の階層を確認するとこんな感じ。多くのコマンド(ユーティリティ)がインストールされていることがわかりました。
似た結果なので折り畳み。クリックで展開します。
% ls -go /opt/homebrew/opt/coreutils/bin total 24104 lrwxr-xr-x 1 6 4 18 23:32 b2sum -> gb2sum lrwxr-xr-x 1 7 4 18 23:32 base32 -> gbase32 lrwxr-xr-x 1 7 4 18 23:32 basenc -> gbasenc lrwxr-xr-x 1 6 4 18 23:32 chcon -> gchcon lrwxr-xr-x 1 7 4 18 23:32 factor -> gfactor -rwxr-xr-x 1 92318 4 18 23:32 g[ -rwxr-xr-x 1 110994 4 18 23:32 gb2sum -rwxr-xr-x 1 93395 4 18 23:32 gbase32 -rwxr-xr-x 1 93379 4 18 23:32 gbase64 -rwxr-xr-x 1 92197 4 18 23:32 gbasename -rwxr-xr-x 1 112611 4 18 23:32 gbasenc -rwxr-xr-x 1 93424 4 18 23:32 gcat -rwxr-xr-x 1 114402 4 18 23:32 gchcon -rwxr-xr-x 1 131490 4 18 23:32 gchgrp -rwxr-xr-x 1 113106 4 18 23:32 gchmod -rwxr-xr-x 1 131650 4 18 23:32 gchown -rwxr-xr-x 1 113411 4 18 23:32 gchroot -rwxr-xr-x 1 182146 4 18 23:32 gcksum -rwxr-xr-x 1 94241 4 18 23:32 gcomm -rwxr-xr-x 1 173119 4 18 23:32 gcp -rwxr-xr-x 1 166291 4 18 23:32 gcsplit -rwxr-xr-x 1 110528 4 18 23:32 gcut -rwxr-xr-x 1 146593 4 18 23:32 gdate -rwxr-xr-x 1 130927 4 18 23:32 gdd -rwxr-xr-x 1 133519 4 18 23:32 gdf -rwxr-xr-x 1 210896 4 18 23:32 gdir -rwxr-xr-x 1 111174 4 18 23:32 gdircolors -rwxr-xr-x 1 92164 4 18 23:32 gdirname -rwxr-xr-x 1 223631 4 18 23:32 gdu -rwxr-xr-x 1 91201 4 18 23:32 gecho -rwxr-xr-x 1 111136 4 18 23:32 genv -rwxr-xr-x 1 94083 4 18 23:32 gexpand -rwxr-xr-x 1 183344 6 4 17:42 gexpr -rwxr-xr-x 1 147536 6 4 17:42 gfactor -rwxr-xr-x 1 91090 4 18 23:32 gfalse -rwxr-xr-x 1 110624 4 18 23:32 gfmt -rwxr-xr-x 1 93217 4 18 23:32 gfold -rwxr-xr-x 1 92819 4 18 23:32 ggroups -rwxr-xr-x 1 109921 4 18 23:32 ghead -rwxr-xr-x 1 92163 4 18 23:32 ghostid -rwxr-xr-x 1 93903 4 18 23:32 gid -rwxr-xr-x 1 175205 4 18 23:32 ginstall -rwxr-xr-x 1 112609 4 18 23:32 gjoin -rwxr-xr-x 1 92449 4 18 23:32 gkill -rwxr-xr-x 1 92417 4 18 23:32 glink -rwxr-xr-x 1 133295 4 18 23:32 gln -rwxr-xr-x 1 92228 4 18 23:32 glogname -rwxr-xr-x 1 210895 4 18 23:32 gls -rwxr-xr-x 1 110275 4 18 23:32 gmd5sum -rwxr-xr-x 1 94258 4 18 23:32 gmkdir -rwxr-xr-x 1 92467 4 18 23:32 gmkfifo -rwxr-xr-x 1 93010 4 18 23:32 gmknod -rwxr-xr-x 1 93811 4 18 23:32 gmktemp -rwxr-xr-x 1 174367 4 18 23:32 gmv -rwxr-xr-x 1 92353 4 18 23:32 gnice -rwxr-xr-x 1 165167 4 18 23:32 gnl -rwxr-xr-x 1 92898 4 18 23:32 gnohup -rwxr-xr-x 1 92530 4 18 23:32 gnproc -rwxr-xr-x 1 128627 4 18 23:32 gnumfmt -rwxr-xr-x 1 113055 4 18 23:32 god -rwxr-xr-x 1 93458 4 18 23:32 gpaste -rwxr-xr-x 1 92244 4 18 23:32 gpathchk -rwxr-xr-x 1 94898 4 18 23:32 gpinky -rwxr-xr-x 1 132111 4 18 23:32 gpr -rwxr-xr-x 1 92005 4 18 23:32 gprintenv -rwxr-xr-x 1 92659 4 18 23:32 gprintf -rwxr-xr-x 1 167264 4 18 23:32 gptx -rwxr-xr-x 1 93680 4 18 23:32 gpwd -rwxr-xr-x 1 111845 4 18 23:32 greadlink -rwxr-xr-x 1 112357 4 18 23:32 grealpath -rwxr-xr-x 1 114399 4 18 23:32 grm -rwxr-xr-x 1 92978 4 18 23:32 grmdir -rwxr-xr-x 1 92163 4 18 23:32 gruncon -rwxr-xr-x 1 93248 4 18 23:32 gseq -rwxr-xr-x 1 110292 4 18 23:32 gsha1sum -rwxr-xr-x 1 110598 4 18 23:32 gsha224sum -rwxr-xr-x 1 110598 4 18 23:32 gsha256sum -rwxr-xr-x 1 110598 4 18 23:32 gsha384sum -rwxr-xr-x 1 110598 4 18 23:32 gsha512sum -rwxr-xr-x 1 113474 4 18 23:32 gshred -rwxr-xr-x 1 113361 4 18 23:32 gshuf -rwxr-xr-x 1 92690 4 18 23:32 gsleep -rwxr-xr-x 1 172545 4 18 23:32 gsort -rwxr-xr-x 1 114034 4 18 23:32 gsplit -rwxr-xr-x 1 132945 4 18 23:32 gstat -rwxr-xr-x 1 93635 4 18 23:32 gstdbuf -rwxr-xr-x 1 111409 4 18 23:32 gstty -rwxr-xr-x 1 94048 4 18 23:32 gsum -rwxr-xr-x 1 92481 4 18 23:32 gsync -rwxr-xr-x 1 148080 4 18 23:32 gtac -rwxr-xr-x 1 112769 4 18 23:32 gtail -rwxr-xr-x 1 93984 4 18 23:32 gtee -rwxr-xr-x 1 91953 4 18 23:32 gtest -rwxr-xr-x 1 93604 4 18 23:32 gtimeout -rwxr-xr-x 1 131778 4 18 23:32 gtouch -rwxr-xr-x 1 110943 4 18 23:32 gtr -rwxr-xr-x 1 91089 4 18 23:32 gtrue -rwxr-xr-x 1 92933 4 18 23:32 gtruncate -rwxr-xr-x 1 93234 4 18 23:32 gtsort -rwxr-xr-x 1 92096 4 18 23:32 gtty -rwxr-xr-x 1 92194 4 18 23:32 guname -rwxr-xr-x 1 94085 4 18 23:32 gunexpand -rwxr-xr-x 1 110881 4 18 23:32 guniq -rwxr-xr-x 1 92387 4 18 23:32 gunlink -rwxr-xr-x 1 110579 4 18 23:32 guptime -rwxr-xr-x 1 92642 4 18 23:32 gusers -rwxr-xr-x 1 210897 4 18 23:32 gvdir -rwxr-xr-x 1 112911 4 18 23:32 gwc -rwxr-xr-x 1 94864 4 18 23:32 gwho -rwxr-xr-x 1 92275 4 18 23:32 gwhoami -rwxr-xr-x 1 92240 4 18 23:32 gyes lrwxr-xr-x 1 7 4 18 23:32 hostid -> ghostid lrwxr-xr-x 1 7 4 18 23:32 md5sum -> gmd5sum lrwxr-xr-x 1 6 4 18 23:32 nproc -> gnproc lrwxr-xr-x 1 7 4 18 23:32 numfmt -> gnumfmt lrwxr-xr-x 1 6 4 18 23:32 pinky -> gpinky lrwxr-xr-x 1 4 4 18 23:32 ptx -> gptx lrwxr-xr-x 1 7 4 18 23:32 runcon -> gruncon lrwxr-xr-x 1 8 4 18 23:32 sha1sum -> gsha1sum lrwxr-xr-x 1 10 4 18 23:32 sha224sum -> gsha224sum lrwxr-xr-x 1 10 4 18 23:32 sha256sum -> gsha256sum lrwxr-xr-x 1 10 4 18 23:32 sha384sum -> gsha384sum lrwxr-xr-x 1 10 4 18 23:32 sha512sum -> gsha512sum lrwxr-xr-x 1 6 4 18 23:32 shred -> gshred lrwxr-xr-x 1 5 4 18 23:32 shuf -> gshuf lrwxr-xr-x 1 7 4 18 23:32 stdbuf -> gstdbuf lrwxr-xr-x 1 4 4 18 23:32 tac -> gtac lrwxr-xr-x 1 8 4 18 23:32 timeout -> gtimeout
date コマンド以外でも、GNU 版を利用したいケースで活用できそうです。
終わりに
Mac 端末でミリ秒以下のタイムスタンプを取得したい、という話でした。
BSD と GNU の違いをぼんやりとしか把握していなかったので、改めて調べ直して少し理解が深まりました。「Mac は BSD と GNU どっちだっけ?」というのを毎回忘れてしまうので、今回の調査で定着していたらいいなと思います。
ミリ秒以下のタイムスタンプはそこそこ需要が大きそうな予感がしますが、標準でインストールされたものだけでは賄いきれないことが少し意外でした。gdate
を利用するのとはまた別のアプローチをとった例もありますので、あわせてご参考ください。
以上、 チバユキ (@batchicchi) がお送りしました。